package com.shop.cereshop.app.tencent.im.service.impl;

import cn.hutool.core.collection.CollUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.shop.cereshop.app.dao.buyer.CereBuyerUserDAO;
import com.shop.cereshop.app.dao.tim.redpacket.RedPacketAllocationMapper;
import com.shop.cereshop.app.dao.tim.redpacket.RedPacketMapper;
import com.shop.cereshop.app.dao.tim.redpacket.RedPacketReceiveRecordMapper;
import com.shop.cereshop.app.dao.tim.redpacket.RedPacketRefundRecordMapper;
import com.shop.cereshop.app.dao.wallet.WalletTransactionDAO;
import com.shop.cereshop.app.domain.user.UserDTO;
import com.shop.cereshop.app.exception.BusinessException;
import com.shop.cereshop.app.tencent.im.delay.constant.BusinessType;
import com.shop.cereshop.app.tencent.im.delay.constant.TaskType;
import com.shop.cereshop.app.tencent.im.delay.model.DelayedTask;
import com.shop.cereshop.app.tencent.im.delay.service.RDelayedQueueService;
import com.shop.cereshop.app.tencent.im.model.redpacket.*;
import com.shop.cereshop.app.tencent.im.pay.TimWalletPayService;
import com.shop.cereshop.app.tencent.im.service.RedPacketService;
import com.shop.cereshop.app.tencent.im.utils.RedPacketUtils;
import com.shop.cereshop.app.utils.SnowflakeIdFactory;
import com.shop.cereshop.commons.constant.CoReturnFormat;
import com.shop.cereshop.commons.domain.wallet.WalletTransaction;
import com.shop.cereshop.commons.enums.PaymentModeEnum;
import com.shop.cereshop.commons.enums.TransactionTypeEnum;
import com.shop.cereshop.commons.result.Result;
import com.shop.cereshop.commons.tencent.im.enums.im.ActionStatus;
import com.shop.cereshop.commons.tencent.im.enums.im.MsgTypeEnum;
import com.shop.cereshop.commons.tencent.im.enums.redpacket.RedPacketAllocationStatusEnum;
import com.shop.cereshop.commons.tencent.im.enums.redpacket.RedPacketLifeCycleEnum;
import com.shop.cereshop.commons.tencent.im.enums.redpacket.RedPacketStatusEnum;
import com.shop.cereshop.commons.tencent.im.enums.redpacket.RedPacketTypeEnum;
import com.shop.cereshop.commons.tencent.im.model.msg.data.CustomMsgData;
import com.shop.cereshop.commons.tencent.im.model.msg.dto.MsgRequestDTO;
import com.shop.cereshop.commons.tencent.im.model.response.SendGroupMsgResponse;
import com.shop.cereshop.commons.tencent.im.model.response.SendMsgResponse;
import com.shop.cereshop.commons.tencent.im.model.response.TIMBaseResponse;
import com.shop.cereshop.commons.tencent.im.service.TimMsgService;
import com.shop.cereshop.commons.utils.redis.RedissonUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.redisson.api.RLock;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.ObjectUtils;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.TimeUnit;
import java.util.function.Function;
import java.util.stream.Collectors;

import static com.shop.cereshop.commons.tencent.im.constant.RedisCache.CACHE_RED_PACKET_RECEIVE_LOCK_PREFIX;
import static java.util.stream.Collectors.toMap;


/**
 * @author zdd
 * @version 1.0
 * @description: 腾讯云IM发 送红包接口实现
 * @date 2025/9/3 14:36
 */
@Slf4j
@Service
public class RedPacketServiceImpl implements RedPacketService {

    /**
     * 锁等待时间
     */
    @Value("${red-packet.lock.wait-time:3}")
    private long lockWaitTime;
    /**
     * 锁自动续期时间
     */
    @Value("${red-packet.lock.lease-time:1}")
    private long lockLeaseTime;
    /**
     * 锁时间单位
     */
    @Value("#{T(java.util.concurrent.TimeUnit).valueOf('${red-packet.lock.time-unit:SECONDS}')}")
    private TimeUnit lockTimeUnit;

    @Resource
    private RedPacketMapper redPacketMapper;
    @Resource
    private RedPacketReceiveRecordMapper redPacketReceiveRecordMapper;
    @Resource
    private RedPacketRefundRecordMapper redPacketRefundRecordMapper;
    @Resource
    private RedPacketAllocationMapper redPacketAllocationMapper;
    @Resource
    private TimMsgService timMsgService;
    @Resource
    private TimWalletPayService timWalletPayService;
    @Resource
    private RDelayedQueueService delayedQueueService;
    @Resource
    private WalletTransactionDAO walletTransactionDAO;
    @Resource
    private CereBuyerUserDAO buyerUserDAO;


    @Transactional(rollbackFor = Exception.class)
    @Override
    public Result<RedPacketVO> create(CreateRedPacketParam packetParam) {
        try {
            // 参数校验
            Result<Object> statusResult = RedPacketUtils.validateCreateRedPacketParams(packetParam);
            if (!CoReturnFormat.SUCCESS.equals(statusResult.getCode())) {
                return Result.custom(statusResult.getCode(), statusResult.getMessage());
            }

            // 生成红包ID、保存红包数据
            String packetId = String.valueOf(SnowflakeIdFactory.generateId());
            packetParam.setPacketId(packetId);
            RedPacket redPacket = packetParam.covertToRedPacket();
            redPacket.setOrderId("RP" + packetId);
            if (StringUtils.isNotBlank(redPacket.getReceiverId())) {
                String receiverName = buyerUserDAO.getUserNameById(Long.valueOf(redPacket.getReceiverId()));
                redPacket.setReceiverName(receiverName);
            }
            redPacketMapper.insert(redPacket);

            // 保存红包分配金额数据
            List<RedPacketAllocation> allocationList = packetParam.covertToRedPacketAllocationList();
            if (CollUtil.isEmpty(allocationList)) {
                log.error("创建红包时分配列表为空, packetParam: {}", packetParam);
                throw BusinessException.of(CoReturnFormat.FAIL, "服务异常，请稍后重试！");
            }
            redPacketAllocationMapper.batchInsert(allocationList);

//        // 缓存红包信息到Redis
//        cacheRedPacket(redPacket);

            log.info("红包创建成功, packetId: {}", packetId);
            RedPacketVO packetVO = redPacket.covertToVO();
            packetVO.setTransactionType(TransactionTypeEnum.RED_PACKET_PAY.getCode());
            packetVO.setPacketLifeCycle(RedPacketLifeCycleEnum.EFFECTIVE.getCode());
            return Result.success(packetVO);
        } catch (BusinessException be) {
            log.warn("创建红包业务异常: {}", be.getMessage());
            throw be;
        } catch (Exception e) {
            log.error("创建红包时发生未预期异常, packetParam: {}", packetParam, e);
            throw new BusinessException("创建红包失败: " + e.getMessage());
        }
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public Result<Void> send(SendRedPacketParam param) {
        String orderId = param.getOrderId();
        if (StringUtils.isBlank(orderId)) {
            return Result.custom(CoReturnFormat.PARAM_INVALID, "请检查参数");
        }
//        RedPacket redPacket = redPacketMapper.selectById(packetId);
        QueryWrapper<RedPacket> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("order_id", orderId);
        RedPacket redPacket = redPacketMapper.selectOne(queryWrapper);
        if (redPacket == null) {
            log.warn("发送红包时，红包不存在, orderId: {}", orderId);
            return Result.custom(CoReturnFormat.FAIL, "红包不存在");
        }
        if (redPacket.getStatus() > 0) {
            return Result.custom(CoReturnFormat.FAIL, "当前红包已发送过，请勿重复发送！");
        }
        String packetId = redPacket.getPacketId();
        String typeName = StringUtils.isNotBlank(redPacket.getGroupId()) ? "群红包" : "单聊红包";
        long buyerUserId = Long.parseLong(redPacket.getSenderId());
        BigDecimal totalAmount = redPacket.getTotalAmount();

        // 校验支付结果
        Result<Void> payResult = verifyPayResult(param.getPayMethod(), buyerUserId, packetId, orderId, totalAmount);
        if (!CoReturnFormat.SUCCESS.equals(payResult.getCode())) {
            return payResult;
        }

        // 设置红包发送时间和过期时间、更新红包状态为 【发送未领取】
        LocalDateTime nowTime = LocalDateTime.now();
        redPacket.setPayMethod(param.getPayMethod());
        redPacket.setStatus(RedPacketStatusEnum.NOT_RECEIVED.getCode());
        redPacket.setSendTime(nowTime);
        redPacket.setExpireTime(nowTime.plusHours(24));

        RedPacketVO packetVO = redPacket.covertToVO();
        packetVO.setPacketLifeCycle(RedPacketLifeCycleEnum.EFFECTIVE.getCode());
        if (StringUtils.isNotBlank(param.getSenderHeadImage())) {
            packetVO.setSenderHeadImage(param.getSenderHeadImage());
        } else {
            String headImage = buyerUserDAO.getHeadImageById(Long.parseLong(redPacket.getSenderId()));
            packetVO.setSenderHeadImage(headImage);
        }
        MsgRequestDTO requestDTO = packetVO.covertToMsgRequestDTO();
        CustomMsgData msgData = packetVO.covertToCustomMsgData();

        boolean isSendSuccess = false;
        try {
            // 发送红包消息
            if (StringUtils.isNotBlank(redPacket.getGroupId())) {
                SendGroupMsgResponse response = timMsgService.sendGroupMsg(MsgTypeEnum.CUSTOM, msgData, requestDTO);
                if (ActionStatus.OK.getStatus().equals(response.getActionStatus())) {
                    isSendSuccess = true;
                    redPacket.setMsgSeq(response.getMsgSeq());
                }
            } else {
                SendMsgResponse response = timMsgService.sendC2cMsg(MsgTypeEnum.CUSTOM, msgData, requestDTO);
                if (ActionStatus.OK.getStatus().equals(response.getActionStatus())) {
                    isSendSuccess = true;
                    // 更新红包的msgId和msgKey
                    redPacket.setMsgId(response.getMsgId());
                    redPacket.setMsgKey(response.getMsgKey());
                }
            }
            if (isSendSuccess) {
                log.info("{}发送成功，接收者ID: {}, 红包ID: {}", typeName, redPacket.getGroupId(), packetId);
                redPacketMapper.updateById(redPacket);

                // 添加到延迟队列，24小时后检查是否需要退款，触发红包过期定时取消任务
                long delaySeconds = java.time.Duration.between(nowTime, redPacket.getExpireTime()).getSeconds();
                DelayedTask task = new DelayedTask(packetId, TaskType.RED_PACKET, redPacket);
                delayedQueueService.addTask(BusinessType.RED_PACKET_EXPIRE_REFUND, task, delaySeconds);
                return Result.success();
            } else {
                // 发送失败需要恢复用户钱包余额
                String desc = "红包消息发送失败，回退用户钱包余额";
                timWalletPayService.addWalletBalance(buyerUserId, packetId, totalAmount, TransactionTypeEnum.RED_PACKET_REFUND.getCode(), desc);

                log.warn("{}发送失败，接收者ID: {}, 红包ID: {}", typeName, redPacket.getReceiverId(), packetId);
                return Result.custom(CoReturnFormat.FAIL, "系统繁忙，请稍后再试!");
            }
        } catch (Exception e) {
            log.error("发送红包消息失败, packetId: {}", packetId, e);
            throw new BusinessException("发送红包消息失败: " + e.getMessage());
        }
    }

    /**
     * 验证支付结果
     *
     * @param payMethod   支付方式
     * @param buyerUserId 用户ID
     * @param packetId    红包ID
     * @param orderId     订单ID
     * @param totalAmount 红包总金额
     * @return 支付结果
     */
    private Result<Void> verifyPayResult(Integer payMethod, long buyerUserId, String packetId, String orderId, BigDecimal totalAmount) {
        // 根据支付方式和红包单号，校验是否已经支付成功（钱包、支付宝、微信分别校验）
        if (PaymentModeEnum.WALLET_PAY.getCode().equals(payMethod)) {
            LambdaQueryWrapper<WalletTransaction> queryWrapper = new QueryWrapper<WalletTransaction>().lambda()
                    .eq(WalletTransaction::getOrderId, orderId)
                    .eq(WalletTransaction::getBuyerUserId, buyerUserId);
            WalletTransaction transaction = walletTransactionDAO.selectOne(queryWrapper);
            if (transaction == null) {
                log.warn("发送红包时，没有查到钱包支付流水, packetId: {}", packetId);
                return Result.custom(CoReturnFormat.FAIL, "钱包支付结果异常，请先支付！");
            }
            boolean isAmountValid = (transaction.getAmount().compareTo(totalAmount) == 0)
                    && (transaction.getBalanceBefore().compareTo(totalAmount) >= 0);
            if (!isAmountValid) {
                log.warn("发送红包时，钱包支付结果异常, packetId: {}", packetId);
                return Result.custom(CoReturnFormat.FAIL, "钱包支付结果异常，请先支付！");
            }

        } else {
//            // 验证支付宝支付结果
//            AlipayTradeQueryResponse queryResponse = AlipayUtils.queryPayResult(orderId);
//            if (!AlipayUtils.isSuccess(queryResponse)) {
//                log.warn("发送红包时，支付宝支付结果异常, packetId: {}", packetId);
//            }
            // todo 微信、支付宝 暂不支持
            return Result.custom(CoReturnFormat.FAIL, "暂不支持该支付方式，请选择其他支付方式！");
        }
        return Result.success();
    }

    @Override
    public Result<Object> receive(ReceiveRedPacketParam param) {
        // 获取分布式锁对象
        RLock lock = RedissonUtil.getLock(CACHE_RED_PACKET_RECEIVE_LOCK_PREFIX + param.getPacketId() + ":" + param.getReceiverId());
        boolean isLocked = false;
        try {
            // 尝试加锁，使用配置的时间和单位
            isLocked = lock.tryLock(lockWaitTime, lockLeaseTime, lockTimeUnit);
            if (!isLocked) {
                log.warn("用户：[ {} ] 领取红包：[ {} ] 时，尝试加锁失败！", param.getReceiverId(), param.getPacketId());
                return Result.custom(CoReturnFormat.FAIL, "系统繁忙，请稍后再试!");
            }

            // 通过AopContext获取当前代理对象，以确保事务能正常工作
            RedPacketServiceImpl proxy = (RedPacketServiceImpl) org.springframework.aop.framework.AopContext.currentProxy();
            return proxy.processReceive(param.getPacketId(), param.getReceiverId(), param.getReceiverName());
        } catch (InterruptedException e) {
            // 保留中断状态
            Thread.currentThread().interrupt();
            log.error("用户：[ {} ] 领取红包：[ {} ] 时，加锁等待被中断！", param.getReceiverId(), param.getPacketId(), e);
            return Result.custom(CoReturnFormat.FAIL, "领取失败，请稍后重试！");
        } catch (Exception e) {
            log.error("用户：[ {} ] 领取红包：[ {} ] 时，发生未预期异常！", param.getReceiverId(), param.getPacketId(), e);
            return Result.custom(CoReturnFormat.FAIL, "领取失败，请稍后重试！");
        } finally {
            // 在 finally 块中确保锁被释放
            if (isLocked && lock.isLocked() && lock.isHeldByCurrentThread()) {
                lock.unlock();
                log.debug("用户 [{}] 领取红包 [{}] 后，锁已释放", param.getReceiverId(), param.getPacketId());
            }
        }
    }

    /**
     * 处理红包领取的核心逻辑，包括状态检查和数据更新。
     * 此方法应在持有锁的情况下被调用，并且需要通过 Spring 代理调用以确保事务生效。
     *
     * @param packetId     红包ID
     * @param receiverId   领取人ID
     * @param receiverName 领取人名称
     * @return 领取结果
     */
    @Transactional(rollbackFor = Exception.class)
    public Result<Object> processReceive(String packetId, String receiverId, String receiverName) {
        try {
            // 判断红包状态和剩余数量
            RedPacket redPacket = redPacketMapper.selectById(packetId);
            if (redPacket == null) {
                return Result.custom(CoReturnFormat.FAIL, "packetId为: " + packetId + "的红包不存在!");
            }
            // 判断红包领取人是否为红包的接收者 (单聊、群聊时指定专属红包 两种情况)
            boolean exclusive = StringUtils.isBlank(redPacket.getGroupId())
                    || (StringUtils.isNotBlank(redPacket.getGroupId())
                        && RedPacketTypeEnum.EXCLUSIVE.getCode().equals(redPacket.getPacketType()));
            if (exclusive) {
                if (!StringUtils.equals(redPacket.getReceiverId(), receiverId)) {
                    return Result.custom(CoReturnFormat.FAIL, "不是您的红包，不能领取!");
                }
            }
            // 查询用户是否已领取过该红包
            LambdaQueryWrapper<RedPacketReceiveRecord> queryRecordWrapper = new QueryWrapper<RedPacketReceiveRecord>().lambda()
                    .eq(RedPacketReceiveRecord::getPacketId, redPacket.getPacketId())
                    .eq(RedPacketReceiveRecord::getReceiverId, receiverId);
            int recordCount = redPacketReceiveRecordMapper.selectCount(queryRecordWrapper);
            Result<Object> statusResult = RedPacketUtils.judgeRedPacketReceiveStatus(redPacket, recordCount);
            if (!CoReturnFormat.SUCCESS.equals(statusResult.getCode())) {
                return statusResult;
            }

            // 如果没有领取过该红包，则查询该红包的分配信息，取出第一个未被领取的金额，并进行领取操作
            LambdaQueryWrapper<RedPacketAllocation> queryAllocationWrapper = new QueryWrapper<RedPacketAllocation>().lambda()
                    .eq(RedPacketAllocation::getPacketId, packetId)
                    .eq(RedPacketAllocation::getReceiveStatus, RedPacketAllocationStatusEnum.NOT_RECEIVED.getCode())
                    .last("LIMIT 1");
            RedPacketAllocation allocation = redPacketAllocationMapper.selectOne(queryAllocationWrapper);
            if (ObjectUtils.isEmpty(allocation)) {
                log.error("红包：[ {} ] 分配信息不存在或已领完!", packetId);
                return Result.custom(CoReturnFormat.FAIL, "手慢了，红包已领完!");
            }
            // 红包分配金额的子红包分配编号，作为领取红包的单号
            String receiveOrderNo = allocation.getAllocationNo();
            BigDecimal receiveAmount = allocation.getAmount();

            // 修改红包状态、剩余数量、剩余金额
            ReceiveRedPacketDTO packetDTO = RedPacketUtils.buildRedPacketDTO(redPacket, receiverId, receiverName, allocation.getAmount());
            int updateCount = redPacketMapper.updateStatus(packetDTO);
            if (updateCount <= 0) {
                log.warn("更新红包状态失败或无匹配记录, DTO: {}", packetDTO);
                throw new BusinessException("领取失败，请重试");
            }

            // 修改红包分配状态
            allocation.setReceiveStatus(RedPacketAllocationStatusEnum.RECEIVED.getCode());
            int allocUpdateCount = redPacketAllocationMapper.updateById(allocation);
            if (allocUpdateCount <= 0) {
                log.warn("更新分配状态失败或无匹配记录, Allocation ID: {}", allocation.getId());
                throw new BusinessException("领取失败，请重试");
            }

            // 保存红包领取记录
            RedPacketReceiveRecord receiveRecord = RedPacketReceiveRecord.builder()
                    .id(String.valueOf(SnowflakeIdFactory.generateId()))
                    .packetId(packetId)
                    .allocationNo(receiveOrderNo)
                    .receiverId(receiverId)
                    .receiverName(receiverName)
                    .amount(receiveAmount)
                    .isLuckiest(allocation.getIsLuckiest())
                    .receiveTime(LocalDateTime.now())
                    .build();
            int insertCount = redPacketReceiveRecordMapper.insert(receiveRecord);
            if (insertCount <= 0) {
                log.warn("插入领取记录失败, Record: {}", receiveRecord);
                throw new BusinessException("领取失败，请重试");
            }

            // 领取红包成功，增加金额到用户钱包
            boolean added = timWalletPayService.addWalletBalance(Long.parseLong(receiverId), receiveOrderNo, receiveAmount,
                    TransactionTypeEnum.RED_PACKET_RECEIVE.getCode(), "领取红包后，增加红包金额到用户钱包。");
            if (!added) {
                log.warn("添加金额到用户钱包失败, userId: {}, amount: {}", receiverId, receiveAmount);
                throw new BusinessException("领取失败，请重试");
            }

            // 发送修改红包历史消息
            boolean modified = modifyTimMsg(packetId);
            if (!modified) {
                log.warn("修改红包历史消息失败, packetId: {}", packetId);
                throw new BusinessException("领取失败，请重试");
            }
            log.info("用户 [{}] 成功领取红包 [{}], 子单号 [{}], 金额: {}", receiverId, packetId, receiveOrderNo, receiveAmount);
            return Result.success(receiveRecord.covertToVO());
        } catch (BusinessException be) {
            log.warn("处理红包领取时发生业务异常: {}", be.getMessage());
            throw be;
        } catch (Exception e) {
            log.error("处理红包领取时发生未预期异常, packetId: {}, receiverId: {}", packetId, receiverId, e);
            throw new BusinessException("领取红包失败: " + e.getMessage());
        }
    }

    @Override
    public boolean modifyTimMsg(String packetId) {
        // 查询最新的红包信息
        RedPacket redPacket = redPacketMapper.selectById(packetId);
        if (redPacket == null) {
            log.error("红包：[{}] 信息不存在，无法修改消息！", packetId);
            return false;
        }
        // 查询已领取的红包的用户ID列表
        LambdaQueryWrapper<RedPacketReceiveRecord> queryWrapper = new LambdaQueryWrapper<RedPacketReceiveRecord>()
                .select(RedPacketReceiveRecord::getReceiverId)
                .eq(RedPacketReceiveRecord::getPacketId, packetId);
        List<RedPacketReceiveRecord> receiveRecords = redPacketReceiveRecordMapper.selectList(queryWrapper);
        List<String> receiverIds = receiveRecords.stream().map(RedPacketReceiveRecord::getReceiverId).collect(Collectors.toList());
        RedPacketVO packetVO = redPacket.covertToVO();
        packetVO.setReceiverIds(receiverIds);
        String headImage = buyerUserDAO.getHeadImageById(Long.parseLong(redPacket.getSenderId()));
        packetVO.setSenderHeadImage(headImage);

        // 设置红包生命周期
        if (RedPacketStatusEnum.NOT_RECEIVED.getCode().equals(redPacket.getStatus()) || RedPacketStatusEnum.NOT_FINISHED.getCode().equals(redPacket.getStatus())) {
            packetVO.setPacketLifeCycle(RedPacketLifeCycleEnum.EFFECTIVE.getCode());
        }
        if (RedPacketStatusEnum.COMPLETED.getCode().equals(redPacket.getStatus())) {
            packetVO.setPacketLifeCycle(RedPacketLifeCycleEnum.COMPLETED.getCode());
        }
        if (RedPacketStatusEnum.EXPIRED.getCode().equals(redPacket.getStatus()) || RedPacketStatusEnum.REFUNDED.getCode().equals(redPacket.getStatus())) {
            packetVO.setPacketLifeCycle(RedPacketLifeCycleEnum.EXPIRED.getCode());
        }
        MsgRequestDTO requestDTO = packetVO.covertToMsgRequestDTO();
        CustomMsgData msgData = packetVO.covertToCustomMsgData();

        requestDTO.setMsgKey(redPacket.getMsgKey());
        requestDTO.setMsgId(redPacket.getMsgId());
        requestDTO.setMsgSeq(redPacket.getMsgSeq());
        TIMBaseResponse response;
        if (StringUtils.isNotBlank(redPacket.getGroupId())) {
            response = timMsgService.modifyGroupMsg(MsgTypeEnum.CUSTOM, msgData, requestDTO);
        } else {
            response = timMsgService.modifyC2cMsg(MsgTypeEnum.CUSTOM, msgData, requestDTO);
        }
        return ActionStatus.OK.getStatus().equals(response.getActionStatus());
    }

    @Override
    public Result<RedPacketDetailVO> detail(String packetId) {
        RedPacket redPacket = redPacketMapper.selectById(packetId);
        if (redPacket == null) {
            return Result.custom(CoReturnFormat.FAIL, "红包不存在!");
        }
        RedPacketVO packetVO = redPacket.covertToVO();
        String headImage = buyerUserDAO.getHeadImageById(Long.parseLong(redPacket.getSenderId()));
        packetVO.setSenderHeadImage(headImage);

        // 查询红包领取记录
        List<RedPacketReceiveRecordVO> recordVos = new ArrayList<>();
        LambdaQueryWrapper<RedPacketReceiveRecord> queryWrapper = new QueryWrapper<RedPacketReceiveRecord>().lambda()
                .eq(RedPacketReceiveRecord::getPacketId, packetId)
                .orderByDesc(RedPacketReceiveRecord::getReceiveTime);
        List<RedPacketReceiveRecord> recordList = redPacketReceiveRecordMapper.selectList(queryWrapper);
        if (CollUtil.isNotEmpty(recordList)) {
            // 查询用户信息
            List<Long> receiveIds = recordList.stream()
                    .map(RedPacketReceiveRecord::getReceiverId)
                    .filter(Objects::nonNull)
                    .map(Long::valueOf)
                    .collect(Collectors.toList());
            Map<String, UserDTO> userMap = getUserMap(receiveIds);
            recordList.forEach(record -> {
                RedPacketReceiveRecordVO recordVO = record.covertToVO();
                if (userMap != null) {
                    UserDTO user = userMap.get(record.getReceiverId());
                    if (user != null) {
                        recordVO.setReceiverHeadImage(user.getHeadImage());
                    }
                }
                recordVos.add(recordVO);
            });
        }

        RedPacketDetailVO detailVO = new RedPacketDetailVO();
        detailVO.setRedPacket(packetVO);
        detailVO.setReceiveRecords(recordVos);
        return Result.success(detailVO);
    }

    private Map<String, UserDTO> getUserMap(List<Long> userIds){
        List<UserDTO> listByIds = buyerUserDAO.getListByIds(userIds);
        if (CollUtil.isEmpty(listByIds)) {
            log.info("用户信息不存在！");
            return null;
        }
        return listByIds.stream()
                .filter(dto -> dto.getBuyerUserId() != null)
                .collect(toMap(
                        dto -> String.valueOf(dto.getBuyerUserId()),
                        Function.identity(),
                        (first, second) -> first
                ));
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public boolean refund(String packetId) {
        // 查询红包信息
        LambdaQueryWrapper<RedPacket> queryWrapper = new QueryWrapper<RedPacket>().lambda()
                .eq(RedPacket::getPacketId, packetId)
                .gt(RedPacket::getRemainAmount, 0L)
                .le(RedPacket::getExpireTime, LocalDateTime.now());
        RedPacket redPacket = redPacketMapper.selectOne(queryWrapper);
        if (redPacket == null) {
            log.info("红包：[{}] 未过期，不用退还至用户钱包余额！", packetId);
            return false;
        }

        BigDecimal refundAmount = redPacket.getRemainAmount();
        log.info("用户：[{}] 发出的红包：[{}] 过期，退还至用户钱包余额", redPacket.getSenderId(), packetId);
        // 退款至用户钱包余额，并记录交易流水
        String refundOrderNo = "REFUND_" + packetId;
        boolean added = timWalletPayService.addWalletBalance(Long.valueOf(redPacket.getSenderId()), refundOrderNo, refundAmount,
                TransactionTypeEnum.RED_PACKET_REFUND.getCode(), "红包退款,红包ID：" + packetId);

        redPacket.setStatus(RedPacketStatusEnum.REFUNDED.getCode());
        int updated = redPacketMapper.updateById(redPacket);
        if (added && updated > 0) {
            RedPacketRefundRecord refundRecord = RedPacketRefundRecord.builder()
                    .id(String.valueOf(SnowflakeIdFactory.generateId()))
                    .packetId(packetId)
                    .refundOrderId(refundOrderNo)
                    .amount(refundAmount)
                    .refundTime(LocalDateTime.now())
                    .build();
            int insert = redPacketRefundRecordMapper.insert(refundRecord);
            if (insert < 1) {
                log.error("添加红包：[{}] 退款记录失败！", packetId);
                return false;
            }
            log.info("用户：[{}] 发出的红包：[{}] 退款成功，退款金额：[{}] ", redPacket.getSenderId(), packetId, refundAmount);
            return true;
        }
        log.error("用户：[{}] 发出的红包：[{}] 退款失败", redPacket.getSenderId(), packetId);
        return false;
    }

    @Override
    public boolean isExpiredAndNotReceived(String packetId) {
        boolean flag = false;
        RedPacket redPacket = redPacketMapper.selectById(packetId);
        if (redPacket == null) {
            return flag;
        }
        boolean isExpired = redPacket.getExpireTime().isBefore(LocalDateTime.now()) &&
                (RedPacketStatusEnum.NOT_RECEIVED.getCode().equals(redPacket.getStatus())
                        || RedPacketStatusEnum.NOT_FINISHED.getCode().equals(redPacket.getStatus())
                        || RedPacketStatusEnum.EXPIRED.getCode().equals(redPacket.getStatus())
                );
        if (isExpired) {
            flag = true;
        }
        boolean isNotReceived = redPacket.getExpireTime().isAfter(LocalDateTime.now()) &&
                (redPacket.getRemainAmount().compareTo(BigDecimal.ZERO) > 0 || redPacket.getRemainAmount().compareTo(BigDecimal.ZERO) == 0);
        if (isNotReceived) {
            flag = true;
        }
        return flag;
    }

    @Override
    public List<RedPacket> getExpiredRedPackets() {
        return redPacketMapper.getExpiredRedPackets();
    }

    @Override
    public RedPacket findByOrderNo(String orderNo, Long buyerUserId) {
        QueryWrapper<RedPacket> queryWrapper = new QueryWrapper<RedPacket>();
        queryWrapper.eq("order_id", orderNo);
        queryWrapper.eq("sender_id", buyerUserId);
        queryWrapper.eq("status", 0);
        return redPacketMapper.selectOne(queryWrapper);
    }
}
